package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 核心容器类
 *
 * @author ztranscript 2021/9/18 10:24
 **/
public class AnnotationConfigApplicationContext {
    /**
     * 配置类
     */
    Class configClazz;

    /**
     * 单例池
     */
    ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    /**
     * beanDefinition 池
     */
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**
     * beanPostProcessor 池
     */
    List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>(16);

    public AnnotationConfigApplicationContext(Class configClazz) {
        this.configClazz = configClazz;
        // 1 解析配置类
        // 1.1 扫描注册 bean
        doScan(configClazz);
        // 1.2 获取配置类中的方法, 如标注了 @Bean 的方法, 为其添加一个 bean

        // 2 生成 singleton Bean
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            String scope = beanDefinition.getScope();
            if (Objects.equals("singleton", scope)) {
                if (!singletonObjects.containsKey(beanName)) {
                    Object bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean);
                }
            }
        });

    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        try {
            // 无参构造创建实例
            Object instance = clazz.getConstructor().newInstance();

            // 属性自动注入
            autowiredByName(clazz, instance);

            // beanNameAware
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // BeanPostProcessor 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // BeanPostProcessor 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void autowiredByName(Class clazz, Object instance) throws IllegalAccessException {
        // 自动注入
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                /*Class<?> fieldType = declaredField.getType();*/
                String fieldName = declaredField.getName();

                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                if (!Objects.equals("", annotation.value())) {
                    fieldName = annotation.value();
                }

                BeanDefinition definition = beanDefinitionMap.get(fieldName);
                if (Objects.isNull(definition)) {
                    throw new RuntimeException("inject field is not a spring bean");
                }

                // TODO: 循环依赖
                Object fieldObj;
                if (Objects.equals("singleton", definition.getScope())) {
                    fieldObj = singletonObjects.get(fieldName);
                    if (Objects.isNull(fieldObj)) {
                        fieldObj = createBean(fieldName, definition);
                        // 添加到单例池
                        singletonObjects.put(fieldName, fieldObj);
                    }
                } else {
                    fieldObj = createBean(fieldName, definition);
                }

                declaredField.setAccessible(true);
                declaredField.set(instance, fieldObj);
            }
        }
    }

    private void doScan(Class configClazz) {
        // 1.获取配置类上的注解,如 @ComponentScan 通过路径注册 bean
        ComponentScan componentScan = (ComponentScan) configClazz.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value().replace(".", "/");
        // 类加载
        ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        if (resource == null) {
            throw new RuntimeException("can not find resource, please check your resource path");
        }
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    String absolutePath = f.getAbsolutePath();
                    if (absolutePath.endsWith(".class")) {
                        String beanClassPathName
                                = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                                .replace("\\", ".");
                        try {
                            // 加载 class
                            Class<?> aClass = classLoader.loadClass(beanClassPathName);
                            // 标注 @Component 注解,注入 bean
                            if (aClass.isAnnotationPresent(Component.class)) {
                                // beanPostProcessor 创建
                                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                    BeanPostProcessor beanPostProcessor
                                            = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                                    beanPostProcessorList.add(beanPostProcessor);
                                }

                                Component component = aClass.getDeclaredAnnotation(Component.class);
                                String className = component.value();
                                if (Objects.equals("", className)) {
                                    String substr = beanClassPathName.substring(beanClassPathName.lastIndexOf(".") + 1);
                                    String firstChar = substr.substring(0, 1).toLowerCase();
                                    className = firstChar + substr.substring(1);
                                }
                                // 注册 bean
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setClazz(aClass);
                                if (aClass.isAnnotationPresent(Scope.class)) {
                                    String scope = aClass.getDeclaredAnnotation(Scope.class).value();
                                    beanDefinition.setScope(scope);
                                } else {
                                    beanDefinition.setScope("singleton");
                                }

                                beanDefinitionMap.put(className, beanDefinition);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    public Object getBean(String className) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(className);
        if (Objects.isNull(beanDefinition)) {
            throw new RuntimeException("the className is not a spring bean");
        }

        if (Objects.equals("singleton", beanDefinition.getScope())) {
            return singletonObjects.get(className);
        } else {
            return createBean(className, beanDefinition);
        }
    }
}
